/*
 * TransactionBO.java
 *
 * Created on March 5, 2007, 4:17 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
 
package edu.byu.isys413.jjenkins.data;

import java.sql.Date;
import java.text.NumberFormat;
import java.util.LinkedList;
import java.util.List;

/**
 * The class is represents the transaction business object. It is created
 * by the transactionDAO. It extends businessObject which contains the ID,
 * alreadyInDatabase and dirty. This class contains the attributes of the transaction
 * lines with all getters and setters and any functionality of the object. It contains
 * a method to print the transaction.
 *
 * @author      Jeff Jenkins
 * @version     1.0
 *
 */
public class TransactionBO extends BusinessObject{
    
    private Date date = null;
    private String status = "pending"; // finished, pending, void
    private String type = null; // Sale or Return
    private PaymentBO payment = null;
    private List transactionList = new LinkedList();
    private String currentOpts = null;
    private double subtotal = 0;
    private double tax = 0;
    private double total = 0;
    private double tax_rate = .05; // This could be set on login, but I think it would be better to hard code it in or set it in a preferences file which could be done later
    private String originalTrans = null; //If this is a return, this will reference the original transaction
    private String customer = null;
    private double change = 0;
    private boolean statusChanged = false;
    
    
    /** Creates a new instance of TransactionBO */
    public TransactionBO(){
    }
    
    /** This method sets the date
     * @return Date date**/
    public Date getDate() {
        return date;
    }
    
    /**
     * This method gets the date
     * 
     * @param date 
     */
    public void setDate(Date date) {
        this.date = date;
        setDirty(true);
    }
    
    /** This method sets the status
     * @return Stauts status**/
    public String getStatus() {
        return status;
    }
    
    /**
     * This method sets the status
     * 
     * @param newstatus 
     * @throws edu.byu.isys413.jjenkins.data.DataException 
     */
    public void setStatus(String newstatus) throws DataException{
        // This is to make sure that someone an change the status to finished, but if the
        // status is already finished, no one can change it and therefore teh DAO
        // won't let it be updated
        if(!(status.equals("Finished"))){
            
            this.status = newstatus;
            setDirty(true);
            statusChanged = true;
            
        }else{
            throw new DataException("You cannot edit a transaction that has a status of finished");
        }
        
        
        
    }
    
    /**
     * This is called from the transaction DAO. It allows you to save changeds to the
     * object if you just set the status to finished. Otherwise if the object
     * has already been saved to the database, you would not be able to change it.
     * @return true if the status has changed
     */
    public boolean getStatusChanged(){
        return statusChanged;
    }
    
    /**
     * set status changed if the status has changed
     * @param b 
     */
    public void setStatusChanged(boolean b){
        this.statusChanged = b;
    }
    
    /** This method gets the type
     * @return String type*/
    public String getType() {
        return type;
    }
    
    /**
     * This method sets the type
     * 
     * @param type 
     */
    public void setType(String type) {
        this.type = type;
        setDirty(true);
    }
    
    /** This method gets the Payement
     * @return PaymentBO payment*/
    public PaymentBO getPayment() {
        return payment;
    }
    
    /**
     * This method sets the Payement
     * 
     * @param payment 
     */
    public void setPayment(PaymentBO payment) {
        this.payment = payment;
        setDirty(true);
    }
    
    /** This method gets the transactionList
     * @return List tranactionList*/
    public List getTransactionList() {
        return transactionList;
    }
    
    /**
     * This method sets the transactionList
     * 
     * @param transactionList 
     */
    public void setTransactionList(List transactionList) {
        this.transactionList = transactionList;
        setDirty(true);
    }
    
    /**
     * This method gets the currentOpts Object
     * 
     * @return CurrentOpts currentOpts
     * @throws edu.byu.isys413.jjenkins.data.DataException 
     */
    public CurrentOptsBO getCurrentOpts() throws DataException {
        return CurrentOptsDAO.getInstance().read(currentOpts);
    }
    
    
      /**This method is sort of a hash. When an or save is called on Transaction,
       * the calling method does not want the whole object, it just wants the id. So
       * this method just returns the id. Also I couln't find a way to have it get the object
       * anyways without requiring additional connections
       * @return string id */
    
   
    public String getCurrentOptsID() {
        
        return currentOpts;
    }
    /**
     * This method sets the currentOpts Object
     * 
     * @param currentOpts 
     */
    public void setCurrentOpts(CurrentOptsBO currentOpts) {
        
        this.currentOpts = currentOpts.getId();
        setDirty(true);
    }
    
    /** This method gets the subTotal
     * @return double */
    public double getSubtotal() {
        return subtotal;
    }
    
    /**
     * This method sets the subTotal
     * 
     * @param subtotal 
     */
    public void setSubtotal(double subtotal) {
        this.subtotal = subtotal;
        setDirty(true);
    }
    
    /** This method gets the subTax
     * @return double */
    public double getTax() {
        return tax;
    }
    
    /**
     * This method sets the subTax
     * 
     * @param tax 
     */
    public void setTax(double tax) {
        this.tax = tax;
        setDirty(true);
    }
    
    /** This method gets the Total
     * @return double */
    public double getTotal() {
        return total;
    }
    
    /**
     * This method  sets the Total
     * 
     * @param total 
     */
    public void setTotal(double total) {
        this.total = total;
        setDirty(true);
    }
    
    /** This method getes the tax rate
     * @return double */
    public double getTax_rate() {
        return tax_rate;
    }
    
    /**
     * This method sets the tax rate
     * 
     * @param tax_rate 
     */
    public void setTax_rate(double tax_rate) {
        this.tax_rate = tax_rate;
        setDirty(true);
    }
    
    /**
     * This method gets the original transaction for returns
     * 
     * @return TransactionBO
     * @throws edu.byu.isys413.jjenkins.data.DataException 
     */
    public TransactionBO getOriginalTrans() throws DataException{
        return TransactionDAO.getInstance().read(originalTrans);
    }
    
      /**
     * This method is sort of a hash. When an or save is called on Transaction,
     * the calling method does not want the whole object, it just wants the id. So
     * this method just returns the id. Also I couln't find a way to have it get the object
     * anyways without requiring additional connections
     * @return string id of original transaction
     */
    
    public String getOriginalTransID() {
        
        return originalTrans;
    }
    
    /**
     * This method gets the original transaction for returns
     * 
     * @param originalTrans 
     * @throws edu.byu.isys413.jjenkins.data.DataException 
     */
    public void setOriginalTrans(TransactionBO originalTrans) throws DataException{
        this.originalTrans = originalTrans.getId();
        setDirty(true);
    }
    
    
    /**
     * This method setss the customer
     * 
     * @param customer 
     */
    public void setCustomer(CustomerBO customer) {
        this.customer = customer.getId();
        setDirty(true);
    }
    
    
    /**
     * This method gets the customer
     * 
     * @return CustomerBO
     * @throws edu.byu.isys413.jjenkins.data.DataException 
     */
    public CustomerBO getCustomer() throws DataException{
        return CustomerDAO.getInstance().read(customer);
    }
    
    
    
      /**
     * This method is sort of a hash. When an or save is called on Transaction ,
     * the calling method does not want the whole object, it just wants the id. So
     * this method just returns the id. Also I couln't find a way to have it get the object
     * anyways without requiring additional connections
     * @return id of customer
     */
    
    public String getCustomerID() {
        
        return customer;
        
    }
    
    
    
    /**
     * this method could be handled in the conroller it just calls the calculate subtotal
     * method, calcualte tax, and calculate total methods so the conroller does not have
     * to do it
     * @throws edu.byu.isys413.jjenkins.data.DataException 
     */
    
    public void endTransaction() throws DataException{
        calculateSubTotal();
        calculateTax();
        calculateTotal();
        if(type.equals("return")){//for processing returns
            subtotal = 0-subtotal;
            tax = 0-tax;
            total = 0-total;
        }
        //System.out.println("I'm ending the trans, dangit!!!! subtotal is (please be ----!) "+subtotal);
    }
    
    /**
     * this method calculates the subTotal. It is called from the endTransaction
     * method
     * @throws edu.byu.isys413.jjenkins.data.DataException 
     */
    
    private void calculateSubTotal() throws DataException{
        subtotal = 0;
        for(int i =0; i< transactionList.size(); i++){
            subtotal += ((TransactionLineBO)transactionList.get(i)).getSubtotal();
            System.out.println("subtotal is " +subtotal);
        }
    }
    
    /**
     * this method calculates the Tax. It is called from the endTransaction
     * method
     */
    private void calculateTax(){
        
        tax = subtotal * tax_rate;
    }
    
    /**this method calculates the Total. It is called from the endTransaction
     * method **/
    private void calculateTotal(){
        
        total = subtotal  + tax;
    }
    
    /**
     * this method adds a transaction line to the transactionLine List
     * 
     * @param line 
     * @throws edu.byu.isys413.jjenkins.data.DataException 
     */
    
    public void addTransactionLine(TransactionLineBO line)throws DataException{
        // this awesome piece of code ensures that the item they are returning
        // is on the original transaction. And it Reads the Coupon!
        
        if(type.equals("Return")){
            boolean accept = false;
            ConceptualCouponBO coupon = null;
            List aList =  this.getOriginalTrans().getTransactionList();
            for(int i = 0; i<aList.size(); i++){
                if(line.getRevenueSourceID().equals(((TransactionLineBO)aList.get(i)).getRevenueSourceID())){
                    accept = true;
                    coupon = ((TransactionLineBO)aList.get(i)).getCoupon();
                    break;
                }
                
                
            }
            if(accept){
                line.setCoupon(coupon);
                transactionList.add(line);
                
            }else{
                throw new DataException("This item is not on the origanal transaction");
            }
            
            
        }else{
            transactionList.add(line);
            setDirty(true);
        }
        
    }
    
    /**
     * this method is used to remove items that the customer decideds not to buy
     * or updates the register before it is finished. It is only assessible if the
     * transaction is pending
     * @param ID 
     */
    public void removeTransactionLine(String ID){
        System.out.println("preparing to remove line "+ID);
        for(int i =0; i< transactionList.size(); i++){
            String listid = ((TransactionLineBO)transactionList.get(i)).getId();
            System.out.println("tlineid = "+listid);
            if(listid.equals(ID)){//for some reason this is never being true...
                System.out.println("inside if statement, removed a line"+ID);
                transactionList.remove(i);
                setDirty(true);
                
                try {
                    TransactionDAO.getInstance().save(this);
                } catch (DataException ex) {
                    System.out.println("error in saving in removetransline");
                    ex.printStackTrace();
                }
                break; // breaks out to save processing resources
            }
        }
        
    }
    
    /**
     * this methode returns the change
     * return: double
     * @return change
     */
    public double getChange() {
        return change;
    }
    
    /**
     * this method sets the change
     * par: double
     * @param change 
     */
    public void setChange(double change) {
        this.change = change;
        setDirty(true);
    }
    /**
     * this method formate the output and prints the reciept
     * @throws edu.byu.isys413.jjenkins.data.DataException 
     */
    
    public void display() throws DataException{
       List li = getReceipt();
       for(int i = 0; i <li.size(); i++){
           System.out.println(li.get(i));
       }
    }
    
    public List getReceipt() throws DataException{
         List lines = new LinkedList();
        
        NumberFormat currency = NumberFormat.getCurrencyInstance();
        
        // Prints the receipt
        lines.add("\n\n*************************************************************************************************************");
        lines.add("\nTransaction Receipt");
        lines.add("MyStuff Inc.");
        
        lines.add("TransactionID: " + this.getId() + "\tEmployee ID: " + this.getCurrentOpts().getEmployee().getId());
        lines.add("\nDate: " + getDate().toString() + "\tStore: " + getCurrentOpts().getStore().getLocation() + "\t" + getType() + ": " + getStatus());
        
        lines.add("-------------------------------------------------------------------------------\n");
        lines.add("Type\t\tProduct Description\t\t\t\t\t\tQuantity\tPrice\t\tDiscounts\tSubtotal");
        
        /*TODO: The revenue source getters and setters need to change when we define revenue source */
        /*TODO: Formate the numbers to two decimal spaces*/
        // This formats the transaction line
        for(int i = 0; i < transactionList.size(); i++){
            TransactionLineBO line =(TransactionLineBO)transactionList.get(i);
            String type = "-";
            String description = "-";
            String quan = "-";
            String price = "-";
            String coupon = "-";
            
            //checks to see if there is a revenue source
            if(line.getRevenueSourceID() != null){
                type = line.getRevenueSource().getType();
                description = line.getRevenueSource().getDescription();
                quan = line.getRevenueSource().getQuantity() + "";
                price = currency.format(line.getRevenueSource().getPrice());
            }
            
            // This add space onto the description to have uniform formatting
            while(description.length()<50){
                description += " ";
            }
            
            
            //checks to see if there is a coupon
            if(line.getCouponID() != null){
                coupon = currency.format(line.getCoupon().getAmount());
            }
            lines.add(" " + type + "\t" + description + "\t" + quan + "\t\t" + price + "\t\t" + coupon + "\t\t" + currency.format(line.getSubtotal()));
        }
        lines.add("\nSubtotal:\t" + currency.format(getSubtotal()));
        lines.add("Tax (" + getTax_rate() +"):\t" + currency.format(getTax()));
        lines.add("Total:   \t" + currency.format(getTotal()));
        
        // Checks to see if there is a payment. There might not be if the transaction is pending
        
        String payment = "-";
        String payType = "";
        if(getPayment() != null){
            payment = currency.format(getPayment().getAmount());
            payType = getPayment().getType();
        }
        // Formats is differntly depending on whether is is a sale, or return
        if(!(getType().equals("Return"))){
            lines.add("Payment:\t" + payment + "\t" + payType);
            lines.add("Change:   \t" + currency.format(getChange()));
        }else{
            
            lines.add("Refund:   \t" + payment + "\t" + payType);
        }
        
        lines.add("\n" + getCustomer().getFirstName() + " " + getCustomer().getLastName() + " thank you for shopping with us!\n");
        
        lines.add("\n*************************************************************************************************************");
        return lines;
    }
    
    
    
    
    
}
